package org.turl.utils;

import org.turl.wrappers.WrapperToObject;
import org.turl.wrappers.Wrapper;
import org.turl.annotations.instanceConverter;
import org.pirx.utils.Utilities;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

/**
 * Created by IntelliJ IDEA.
 * User: tonyx
 * Date: May 2, 2008
 * Time: 5:37:41 PM
 * To change this template use File | Settings | File Templates.
 */
public class ModularFactory {

    protected Class abstractionClass;
    protected String referenceImplementation;
    protected String currentImplementation;

    public ModularFactory(Class abstractionClass) {
        this.abstractionClass = abstractionClass;
    }

    protected Map<String,Constructor> implementations=new HashMap<String,Constructor>();

    public void putImplementation(String implementationName, Constructor implementation) {
        if(!checkCompliancy(implementation))  {
            System.out.println("Warning: "+implementation +" does not implement, or is subclass of, " + abstractionClass);
        }
        implementations.put(implementationName,implementation);
    }

    public boolean checkCompliancy(Constructor constructor)  {
        Class theClass=constructor.getDeclaringClass();
        Class[] interfaces = theClass.getInterfaces();
        Set setInterfaces = new HashSet();

        for (int i = 0; i < interfaces.length; i++) {
               setInterfaces.add(interfaces[i]);
        }

        return (Utilities.isAncestor(constructor.getDeclaringClass(),abstractionClass)||setInterfaces.contains(abstractionClass));
    }


    public void setRereferenceImplementation(String implementationName) {
        if (implementations.get(implementationName)==null) {
            throw new RuntimeException("implementation does not exist "+implementationName);
        }
        this.referenceImplementation=implementationName;
    }

    public void setCurrentImplementation(String implementationName) {
        if (implementations.get(implementationName)==null) {
            throw new RuntimeException("implementation does not exist "+implementationName);
        }
        this.currentImplementation =implementationName;
    }

    public String getCurrentImplementation() {
        return currentImplementation;
    }

    public Object getObject(Object[] parameters) throws Exception {
        if ("".equals(currentImplementation)|| currentImplementation ==null) {
            throw new RuntimeException("currentImplementation not set");
        }
        if ("".equals(referenceImplementation)||referenceImplementation==null) {
            throw new RuntimeException("referenceImplementation not set");
        }

        if (referenceImplementation.equals(currentImplementation)) {
            return (implementations.get(currentImplementation)).newInstance(parameters);
        } else {
            return getConverted(implementations.get(currentImplementation),implementations.get(referenceImplementation),parameters);
        }
    }

    private static Object getConverted(Constructor targetConstructor,Constructor originConstructor,Object[] originParams) {
        try {
            Class wrapperMap = (originConstructor.getAnnotation(instanceConverter.class).instanceConverterMap());
                    
            WrapperToObject wrapper = (WrapperToObject) wrapperMap.newInstance();
            Class[] originParamClass = new Class[originParams.length];
            for (int i = 0; i < originParams.length; i++) {
                originParamClass[i] = originParams.getClass();
            }
            Wrapper myWrapper = ((wrapper.getWrapperForClassConstructorWithParameters(targetConstructor.getDeclaringClass(), targetConstructor.getParameterTypes(), originParamClass)));
            Object[] targetPars = myWrapper.wrap(originParams);
            return targetConstructor.newInstance(targetPars);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
